#include "trace.h"

#include <chrono>
#include <cstdio>
#include <list>
#include <string>

#include <unistd.h>

class TraceContent {
public:
    static TraceContent &GetInstance()
    {
        static TraceContent tc;
        return tc;
    }

    ~TraceContent()
    {
        if (disable_) {
            return;
        }

        auto fp = fopen(filename_.c_str(), "w");
        if (fp == nullptr) {
            return;
        }

        auto pid = getpid();
        auto tid = gettid();

        fprintf(fp, "# tracer: nop\n");
        for (const auto &[timeUs, content] : contents_) {
            fprintf(fp, "thread-%d (%d) [000] .... %.6lf: tracing_mark_write: %c|%d|%s\n",
                    tid, pid, timeUs / 1e6, content[0], pid, content.substr(1).c_str());
        }

        fclose(fp);
    }

    void AppendContent(char action, const std::string &content)
    {
        auto nowUs = std::chrono::duration_cast<std::chrono::microseconds>(
            std::chrono::steady_clock::now().time_since_epoch()).count();
        std::string c;
        c.push_back(action);
        c.insert(c.end(), content.begin(), content.end());
        contents_.push_back(std::make_pair(nowUs, std::move(c)));
    }

    void SetTraceFilename(const std::string &filename)
    {
        filename_ = filename;
    }

    void DisableTrace()
    {
        disable_ = true;
    }

private:
    std::list<std::pair<uint64_t, std::string>> contents_;
    std::string filename_ = "1.trace";
    bool disable_ = false;
};

void Trace::SetTraceFilename(const std::string &filename)
{
    TraceContent::GetInstance().SetTraceFilename(filename);
}

void Trace::DisableTrace()
{
    TraceContent::GetInstance().DisableTrace();
}

void Trace::Start(const std::string &proc)
{
    TraceContent::GetInstance().AppendContent('B', proc);
}

void Trace::Finish()
{
    TraceContent::GetInstance().AppendContent('E', "");
}

void Trace::Count(const std::string &key, int val)
{
    TraceContent::GetInstance().AppendContent('C', key + "|" + std::to_string(val));
}
